/**
 * window classes 
 */

// tracking mechanism
myos.windows = {

	// initialize object
	init : function(taskbar, container, options) {
		this.taskbar = taskbar;
		this.container = container;
		
		this.taskbarScroller = false;
		if (typeOf(options.scroller) != "null") {
			// create scroller
			this.taskbarScroller = new Scroller(this.taskbar, {area: 50, velocity: .25});
			// start listener
			this.taskbarScroller.start();
		}
		
		this.taskbarSorter = false;
		if (typeOf(options.sorter) != "null") {
			// create sorter for anchors
			this.taskbarSorter = new Sortables(this.taskbar,{
					clone:true, 
					revert:true, 
					opacity:.2,
					onComplete: function(el,event) {el.removeStyle('opacity');},
					preventDefault: true
				}
			);
		}
	},

	// Window listing (taskbar)	
	taskbar: null,
	setList: function(taskbar) {this.taskbar = taskbar;},
	taskbarScroller: null,
	
	// Window container
	container: null,
	setContainer: function(cntnr) {this.setContainer(cntnr)},
	
	// window states	
	states: {
		MINIMIZED: 0,
		ACTIVE: 1,
		MAXIMIZED: 2
	},

	// container for existing windows
	all: {},
	// window count (distributes new IDs)
	counter: 0,
		
	// current layer count
	layerCount: 10,
	layers: {},
	
	// bump layer
	bumpLayer: function(widg) {
		this.layerCount++;
		this.layers[this.layerCount] = widg;
		return this.layerCount;
	},
	// get top layer
	getTop: function() {
		return this.layers[this.layerCount];	
	},
	// add a window to the listing
	add: function(widg) {
		this.all[widg.windowId] = widg;
	},
	// remove a window from the "all" list
	remove: function(wid) {
		wig = this.all[wid];
		delete this.layers[wig.layer];
		delete this.all[wid];	
	},
	
	// get windows by specified state
	getByState: function(stt) {
		ws = {};
		for (w in this.all) {
			if (this.all[w].state == stt) ws[w] = this.all[w];
		}
		return ws;	
	},
	// shortcuts for state
	getMinimized: function() {return this.getByState(this.states.MINIMIZED);},
	getActive: function() {return this.getByState(this.states.ACTIVE);},
	getMaximized: function() {return this.getByState(this.states.MAXIMIZED);},
	
	// close all windows
	closeAll: function() {
		for (w in this.all) {
			this.all[w].close();
		}
	}
	
}

/**
 * All actions associated with window listing
 */

myos.Window = new Class({
	Implements: [Options, Events],
	
	options: {
		/*
		 *  set options
		 * {
		 * 	element: (required) class of the content element
		 * 	elements: (optional) Array of classes for wrapper divs
		 *  dragHandle: (optional) if draggable, the class to represent the handle
		 *  resizeHandle: (optional) if resizable,.... ^ 
		 * }
		 */
		
		'element': null,
		'top': null,
		'left': null,
		'width': null,
		'height': null,
		'content': null,
		
		'fadeBGWindows': false,
		
		'group': null,
		
		'draggable': true,
		'dragOptions': {},
		'dragHandle': null,
		
		'resizable': true,
		'resizeOptions': {},
		'resizeHandle': null,
		
		'minimizable': false,
		'minClass': null,
		'minClassFocus': null,
		'onMinimize': null,
		
		'maximizable': false,
		'maxClass': null,
		'maxClassFocus': null,
		'onMaximize': null,
		// maximum dimension adjustments
		'margins': {
			top: 0,
			left: 0,
			right: 0,
			bottom: 0
		},

		'restoreClass': null,
		'restoreClassFocus': null,
		'onRestore': null,
		
		'closable': false,
		'closeClass': null,
		'closeClassFocus': null,
		'onClose': null,
		
		'hasOptions': false,
		'optionsClass': null,
		'optionsClassFocus': null,
		'optionMenu': null,
		
		'taskbarAnchor': null,
		'taskbarAnchorMin': null,
		'taskbarAnchorFocus': null,
		'anchorOptions': {
			'closeClass': null,
			'titleClass': null	
		},
					
		'title': null,
		'titleClass': null,
			
		//content element options
		CEOpts: {}
	},
	
	initialize: function(options) {

		// set the window id
		this.setId();
		// set options
		this.setOptions(options);

		// if draggable, set container
		if (this.options.draggable) this.options.dragOptions.container = myos.windows.container;
		// if resizable, set container
		if (this.options.resizable) {
			maxX = myos.windows.container.getSize().x-this.options.margins.right-this.options.margins.left;
			maxY = myos.windows.container.getSize().y-this.options.margins.top-this.options.margins.bottom;
			this.options.resizeOptions.limit = {x:[0,maxX],y:[0,maxY]};
		}
		// track this window
		myos.windows.add(this);
	},
	
	// window coordinates
	coordinates: {},
	saveCoordinates: function(_state) {
		st = _state ? _state : this.getCurrentState();
		coords = Object.clone(this.element.getCoordinates(myos.windows.container));
		coords.top-=this.options.margins.top;
		coords.left-=this.options.margins.left;
		this.coordinates[st] = coords;
	},
	getCoordinates: function(_state) {
		if (_state) return this.coordinates[_state];
		else return this.coordinates[this.getCurrentState()]; 
	},
	
	// object attributes
	states: [myos.windows.states.CLOSED],
	setState: function(st) {
		this.states.push(st);
		//this.saveCoordinates(st);
	},
	getCurrentState: function() {return this.states[this.states.length-1];},
	getPreviousState: function() {return this.states[this.states.length-2];},
	
	setId: function() {
		// bump counter and set id
		myos.windows.counter++;
		this.windowId = myos.windows.counter;
	},

	create: function() {

		this.element = new Element('div',{'class':this.options.element,'id':this.options.element+'_'+this.windowId})
							.inject(myos.windows.container);

		this.content_element = new Element('div',{'styles':{
			position:'relative',
			width:'100%',
			height:'100%',
		}}); 
		
		this.element.grab(this.content_element);

		// set load options
		this.content_element.set('load',this.options.CEOpts);
		
		if (this.options.top) this.element.setStyle("top",this.options.top);
		if (this.options.left) this.element.setStyle("left",this.options.left);
		if (this.options.width) this.element.setStyle("width",this.options.width);
		if (this.options.height) this.element.setStyle("height",this.options.height);
		
		//alert(this.windowId);
		
		rpr = this.options.elements;
		if (rpr) {
			mkdiv = function(nxt) {
				new Element('div',
					{'class': nxt, 'id':nxt.replace(" ","_")+'_'+this.windowId}
				).inject(this.element);
			};
			rpr.each(mkdiv,this);
		}
		
		// make it draggable
		if (this.options.draggable) this.makeDraggable();
		// make it resizable
		if (this.options.resizable) this.makeResizable();
		// give it window options
		if (this.options.hasOptions) this.createWindowOptions();
		// make it minimizable
		if (this.options.minimizable) this.makeMinimizable();
		// make it maximizable
		if (this.options.maximizable) this.makeMaximizable();
		// make it closable
		if (this.options.closable) this.makeClosable();
		
		// create focus events
		focusEvent = function(el) {
			el.addEvent('mousedown',this.focus.bind(this));
		}.bind(this);
		this.element.getChildren().each(focusEvent,this);	
		this.element.addEvent('mousedown',this.focus.bind(this));
		
		// add to window taskbar
		//alert (this.group.taskbarAnchor+" - "+this.taskbarAnchor);
		this.createTaskbarAnchor();
		
		// set current state to active		
		this.setState(myos.windows.states.ACTIVE);		

		// save current coordinates
		this.saveCoordinates();	
		
		// make adjustments
		//this.adjustCoordinates();
		
		this.focus();
		
		return this;
	},
	
	addMenuOpt: function(opts,prnt) {
		if (typeOf(this.optMenu) == 'null') {
			this.optMenu = new Element('ul');
		} 
		omen = this.getOptMenu();	
	},
	
	adjustCoordinates: function() {
		// adjust coordinates
		wcods = this.element.getCoordinates(myos.windows.container);
		ccods = myos.windows.container.getCoordinates();
		
		if ((wcods.width+wcods.left) > ccods.width) {
			this.element.setStyle('width',ccods.width-((wcods.width+wcods.left)-ccods.width));
		} 
		
		if ((wcods.top+wcods.height) > ccods.height) {
			this.element.setStyle('height',ccods.height-((wcods.height+wcods.top)-ccods.height));
		} 
		
	},
	
	createTaskbarAnchor: function() {
		windowObj = (this.group != null) ? this.group : this;
		
		if (typeOf(windowObj.taskbarAnchor) == 'null') {
			ttl = (this.options.title) ? this.options.title : '';
			windowObj.taskbarAnchor = new Element('div',{'class':this.options.taskbarAnchor,'title':ttl});	
			windowObj.taskbarAnchor.setStyle('display','none');
			windowObj.windowList = myos.windows.taskbar;
			windowObj.windowList.grab(windowObj.taskbarAnchor,'top');
			
			windowObj.taskbarAnchor.reveal();
			
			windowObj.anchorAction = function() {
				if (this.isFocused()) {
					this.minimize();
				} else {
					this.focus();
				}
			}.bind(windowObj);
			
			windowObj.taskbarAnchor.addEvent('click',windowObj.anchorAction);
	
			// add to taskbar sorter if exists
			if (myos.windows.taskbarSorter)
				myos.windows.taskbarSorter.addItems(windowObj.taskbarAnchor);
	
			// add close button onmouseover
			if (this.options.anchorOptions.closeClass) {
				windowObj.anchorClose = new Element('div',
					{'class':this.options.anchorOptions.closeClass}
				).inject(windowObj.taskbarAnchor);
				destroyByAnchor = function() {
					this.destroy();
					return false;
				}.bind(windowObj);
				windowObj.anchorClose.addEvent('click',destroyByAnchor);
	
				windowObj.anchorClose.setStyle('opacity',0);
				windowObj.anchorClose.hide();
				showClose = function() {
					this.anchorClose.show();
					twn = new Fx.Tween(this.anchorClose,{duration: 250, transition: Fx.Transitions.Sine.easeOut})
					twn.start('opacity',1);
					
				}.bind(windowObj);
	
				hideClose = function() {
					hd = function() {
						this.anchorClose.hide();
					}.bind(windowObj);
					
					twn = new Fx.Tween(this.anchorClose,{duration: 250, transition: Fx.Transitions.Sine.easeOut, onComplete:hd})
					twn.start('opacity',0);
					
				}.bind(windowObj);
				
				windowObj.taskbarAnchor.addEvent('mouseenter',showClose);
				windowObj.taskbarAnchor.addEvent('mouseleave',hideClose);
	
				if (this.options.title) {
					ankr = this.getTaskbarAnchor();
					ttip = new Tips(ankr,{className:this.options.anchorOptions.titleClass});
					ankr.store('ttip',ttip);
					ttip.attach(ankr);
				}
			}					
		}
	},
	
	getTaskbarAnchor: function() {
		windowObj = (this.group != null) ? this.group : this;
		return windowObj.taskbarAnchor;
	},
	
	// make the window draggable
	makeDraggable: function() {
		if (this.options.dragHandle) { 
			if (this.options.elements.contains(this.options.dragHandle))
				this.options.dragOptions['handle'] = $(this.options.dragHandle.replace(" ","_")+'_'+this.windowId);
			else 
				this.options.dragOptions['handle'] = 
					new Element('div',
						{'class':this.options.dragHandle,'id':this.options.dragHandle.replace(" ","_")+'_'+this.windowId}
						).inject(this.element); 
		}
		this.element.makeDraggable(this.options.dragOptions);
	},
	// make the window resizable
	makeResizable: function() {
		if (this.options.resizeHandle) { 
			if (this.options.elements.contains(this.options.resizeHandle))
				this.options.resizeOptions['handle'] = $(this.options.resizeHandle.replace(" ","_")+'_'+this.windowId);
			else 
				this.options.resizeOptions['handle'] = 
					new Element('div',
						{'class':this.options.resizeHandle,'id':this.options.resizeHandle.replace(" ","_")+'_'+this.windowId}
						).inject(this.element); 
		}
		this.element.makeResizable(this.options.resizeOptions);
	},
	// allow window options 
	createWindowOptions: function() {
		this.optionsButton = 
			new Element('div',
				{'class':this.options.optionsClass,'id':this.options.optionsClass.replace(" ","_")+'_'+this.windowId}
				).inject(this.element); 
		
		if (this.options.optionsClassOn) {
			mover = function() {
					this.optionsButton.addClass(this.options.optionsClassOn);
					this.optionsButton.addEvent('mouseout',mout);
				}.bind(this);
				 
			mout = function(){this.optionsButton.removeClass(this.options.optionsClassOn);}.bind(this);
			
			this.optionsButton.addEvent('mouseover',mover);
		}

		// bind to option display
		this.optionsButton.addEvent('click',this.displayOptions.bind(this));
	},
	// get the content Element object
	getContentElement: function() { return this.content_element; },
	
	// display window options
	displayOptions: function() {
		
	},
	
	// make the window minimizable
	makeMinimizable: function() {
		this.minButton = 
			new Element('div',
				{'class':this.options.minClass,'id':this.options.minClass.replace(" ","_")+'_'+this.windowId}
				).inject(this.element); 

		if (this.options.minClassOn) {
			mover = function() {
					this.minButton.addClass(this.options.minClassOn);
					this.minButton.addEvent('mouseout',mout);
				}.bind(this);
				 
			mout = function(){this.minButton.removeClass(this.options.minClassOn);}.bind(this);
			
			this.minButton.addEvent('mouseover',mover);
		}
		// bind to minimize
		this.minButton.addEvent('click',this.minimize.bind(this));
		
	},
	
	// minimize the window
	minimize: function(ev, opts) {
		windowObj = (this.group) ? this.group : this;
		ankr = windowObj.taskbarAnchor;
		
		this.saveCoordinates();
		
		this.anchorClickEvent = function() {
			this.element.hide();
			ankr.removeEvent('click',this.anchorAction);
			
			ankr.removeClass(this.options.taskbarAnchorFocus);		
			ankr.addClass(this.options.taskbarAnchorMin);		
			this.restoreBound = this.restoreBound ? this.restoreBound : windowObj.restore.bind(windowObj);
			ankr.addEvent('click',this.restoreBound);
			
			// call callback if exists
			if (this.options.onMinimize) this.fireEvent('minimize',this);		
		}.bind(this);
		
		tp = ankr.getCoordinates(myos.windows.taskbar).top;
		morphOpts = {'top':tp,'left':0,'width':100,'height':100,opacity:0};
		morph1 = new Fx.Morph(this.element,{duration: 300, transition: Fx.Transitions.Sine.easeOut,onComplete:this.anchorClickEvent});
		morph1.start(morphOpts);

		this.setState(myos.windows.states.MINIMIZED);
		
		//myos.windows.getPrevious().focus();
	},

	// make the window maximizable
	makeMaximizable: function() {
		this.maxButton = 
			new Element('div',
				{'class':this.options.maxClass,'id':this.options.maxClass.replace(" ","_")+'_'+this.windowId}
				).inject(this.element); 

		if (this.options.maxClassOn) {

			mout = function(){this.maxButton.removeClass(this.options.maxClassOn);}.bind(this);
			
			mover = function() {
					this.maxButton.addClass(this.options.maxClassOn);
					this.maxButton.addEvent('mouseout',mout);
				}.bind(this);
				 
			this.maxButton.addEvent('mouseover',mover);
		}
		
		// bind to maximize
		this.maxButtonClick = this.maximize.bind(this);
		this.maxButton.addEvent('click',this.maxButtonClick);
	},
	
	// maximize the window
	maximize: function() {
		// save the current coordinates
		this.saveCoordinates();	
			
		// get the maximum coordinates
		maxCoords = myos.windows.container.getCoordinates();
		// set morph options with maximize adjustments
		morphOpts = {'top':0,
					'left':0,
					'width':maxCoords.width-this.options.margins.right-this.options.margins.left,
					'height':maxCoords.height-this.options.margins.top-this.options.margins.bottom};
		// morph it			
		this.element.set('morph',{duration: 250, transition: Fx.Transitions.Sine.easeOut});
		this.element.morph(morphOpts);
		
		// swap to restore button if available
		if (this.options.restoreClass) {
			this.maxButton.set('class',this.options.restoreClass);	
			if (this.options.restoreClassOn) {
				mover = function() {
						this.maxButton.addClass(this.options.restoreClassOn);
						this.maxButton.addEvent('mouseout',mout);
					}.bind(this);
					 
				mout = function(){this.maxButton.removeClass(this.options.restoreClassOn);}.bind(this);
				
				this.maxButton.addEvent('mouseover',mover);
			}

		}
		
		// update events to restore
		this.maxButton.removeEvent('click',this.maxButtonClick);
		this.restoreBound = this.restoreBound ? this.restoreBound : this.restore.bind(this);
		this.maxButton.addEvent('click',this.restoreBound);
		
		// set current state
		this.setState(myos.windows.states.MAXIMIZED);
		
		this.focus();
	},
	
	restore: function(ev, opts) {
		
		this.element.show();
		//this.focus();
		
		cs = this.getCoordinates(this.getPreviousState());
		
		/*
		 * If currently maximized, always set the state to active
		 */
		if (this.getCurrentState() == myos.windows.states.MAXIMIZED) {
			
			cs = this.getCoordinates(myos.windows.states.ACTIVE);
			
			this.maxButton.set('class',this.options.maxClass);
			this.maxButton.removeEvent('click',this.restoreBound);
			this.maxButton.addEvent('click',this.maxButtonClick);
		
			this.setState(myos.windows.states.ACTIVE);
		
		/*
		 * If currently minimized, restore to the previous state
		 */
		} else {
			// if previous state was "maximized", rebuild "restore" button
			if (this.getPreviousState() == myos.windows.states.MAXIMIZED) {
				this.maxButton.set('class',this.options.restoreClass);
				this.maxButton.removeEvent('click',this.maxButtonClick);
				this.maxButton.addEvent('click',this.restoreBound);
			// otherwise, previous state was "active".  rebuild "maximize" button.
			} else {
				this.maxButton.set('class',this.options.maxClass);
				this.maxButton.removeEvent('click',this.restoreBound);
				this.maxButton.addEvent('click',this.maxButtonClick);
				
			}
			this.setState(this.getPreviousState());
		}
		
		// bind save coordinates function to this
		saveCoords = this.saveCoordinates.bind(this);
		
		morphOpts = {'top':cs.top,'left':cs.left,'width':cs.width,'height':cs.height,'opacity':1};
		this.element.set('morph',{duration: 500, transition: Fx.Transitions.Sine.easeOut,onComplete:saveCoords});
		this.element.morph(morphOpts);

		//alert('should see the div!');
		//this.windowList.removeChild(this.taskbarAnchor);
		
		this.getTaskbarAnchor().removeClass(this.options.taskbarAnchorMin);		
		this.getTaskbarAnchor().removeEvent('click',this.restoreBound);	
		this.getTaskbarAnchor().addEvent('click',this.anchorAction);
		
		this.focus();
		//alert('restored.');
	},
		
	// make the window closable
	makeClosable: function() {
		this.closeButton = 
			new Element('div',
				{'class':this.options.closeClass,'id':this.options.closeClass.replace(" ","_")+'_'+this.windowId}
				).inject(this.element); 

		if (this.options.closeClassOn) {
			mover = function() {
					this.closeButton.addClass(this.options.closeClassOn);
					this.closeButton.addEvent('mouseout',mout);
				}.bind(this);
				 
			mout = function(){this.closeButton.removeClass(this.options.closeClassOn);}.bind(this);
			
			this.closeButton.addEvent('mouseover',mover);
		}
		// bind to close
		this.closeButton.addEvent('click',this.destroy.bind(this));
	},
	
		
	/******************************************************************************
	 * 
	 * TODO - 
	 * 		
	 * 	Once element has focus, the window taskbar icon should minimize instead
	 * 
	 */	
	// bring the window into focus
	focus: function() {
		// update all to blur
		for (wg in myos.windows.all)
			myos.windows.all[wg].blur();

		this.element.set('tween',{duration:250,transition: Fx.Transitions.Sine.easeOut});
		this.element.tween('opacity',1);

		// set style if exists
		if (this.options.taskbarAnchorFocus) this.getTaskbarAnchor().addClass(this.options.taskbarAnchorFocus);
		
		if (this.isFocused()) return;

		// bring to the front
		this.layer = myos.windows.bumpLayer(this);
		this.element.setStyle('z-index',this.layer);
		
	},
	
	blur: function() {
		if (this.options.taskbarAnchorFocus)
			this.getTaskbarAnchor().removeClass(this.options.taskbarAnchorFocus);

		if (this.options.fadeBGWindows) {		
			this.element.set('tween',{duration:250,transition: Fx.Transitions.Sine.easeOut});
			this.element.tween('opacity',this.options.fadeBGWindows);
		}
			
	},

	isFocused: function() {
		return this.element.getStyle('z-index') == myos.windows.layerCount;
	},	
	
	// remove window an all hildren
	close: function() {this.destroy();},
	destroy: function(ev,opts) {
		this.getTaskbarAnchor().nix({duration:250},true);
		// add to taskbar sorter if exists
		if (myos.windows.taskbarSorter)
			myos.windows.taskbarSorter.removeItems(this.getTaskbarAnchor());
		
		dstroy = this.element.destroy.bind(this.element);
		dtween = new Fx.Tween(this.element,
			{
				duration: 250, 
				transition: Fx.Transitions.Sine.easeOut,
				onComplete: dstroy
			}
		);
		dtween.start('opacity',0);
		
		myos.windows.remove(this.windowId);
		
		try {
			ttip = this.getTaskbarAnchor().retrieve('ttip'); 
			ttip.hide();
			ttip.detach()
		} catch(e) {}
		
		this.fireEvent('close',this);
		
		// stop propagation
		//return false;
	},
	
	setStyles: function(stiles) {
		this.element.setStyles(stiles);	
	},
	
	/*****************************************************************************
	 * Content manipulation
	 */
	// set the content element
	setContent: function(cont) {
		$(cont).inject(this.content_element);		
	},
	
	empty: function() {
		this.content_element.empty();
	},
	
	load: function(url,loadOpts) {
		this.getContentElement().set('load',Object.clone(loadOpts));
		this.getContentElement().load(url);
	}

});

var WindowGroup = new Class({
	Implements: [Options, Events],
	
	options: {
		uri: null,
		pages: {},
		titles: null,
		max: 100,
		windowClass: myos.Window,
		
		onLoad: null
	},
	
	initialize: function(options) {
		this.setOptions(options);	
	},
	
	windows: [],
	current: 0,
	
	add: function(win,ndx) {
		if (arguments.length == 1) {
			ndx = this.windows.length;
		} 
		this.windows[ndx] = win;	
	},
	
	begin: function() {
		return this.goTo(0); 
	},
	
	next: function(curr) {
		this.current = curr;
		return this.bump(1);
	},	
	
	previous: function(curr) {
		this.current = curr;
		return this.bump(-1);
	},
	
	goTo: function (ndx) {
		this.current = ndx;
		return this.bump(0);	
	},
	
	bump: function(factor) {
		if (this.windows[this.current+factor]) {
			this.windows[this.current+factor].restore();
		} else {
			windowOpts = {
				top: (this.current+factor)*20,
				left: (this.current+factor)*20,
				onClose: this.removeWindow.bind(this)	
			};
			
			if (this.options.titles) windowOpts.title = this.options.titles[this.current+factor];
			//alert(this.taskbarAnchor);
			this.windows[this.current+factor] = new this.options.windowClass(windowOpts);
			
			/**
			 * TODO - Add window group taskbar modifications
			 */
			//this.windows[this.current+factor].group = this;
			
			this.windows[this.current+factor].create()
			if (this.options.uri) {
				this.windows[this.current+factor].load(this.options.uri+this.options.pages[this.current+factor].toString());
			}
				
		}
		this.current+=factor;
		return this.windows[this.current+factor];
	},
	
	removeWindow: function(wndo) {
		this.windows[this.windows.indexOf(wndo)]=null;
	},
	
	minimize: function() {
		this.windows.each(function(win) {
			try {
				win.minimize();
			} catch(e) {}
		});
	},
	
	restore: function() {
		this.windows.each(function(win) {
			try {
				win.restore();
			} catch(e) {}
		});
	},
	
	isFocused: function() {
		rv = false;
		for (w=0;w<this.windows.length;w++) {
			try {
				if (this.windows[w].isFocused()) rv = true;
			} catch(e) {}
		}
		return rv;
	},
	
	focus: function() {
		this.windows.each(function(win) {
			try {
				win.focus();
			} catch(e) {}
		});
	},
	
	destroy: function() {
		this.windows.each(function(win) {
			try {
				win.destroy();
				delete this.taskbarAnchor;
			} catch(e) {}
		});
	}
	
});

	